package com.coolw.toolkit.commonspool2;

import com.coolw.toolkit.commonspool2.config.DataSourceConfig;
import com.coolw.toolkit.date.DateUtil;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

import java.sql.Connection;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 自定义数据库连接池测试
 *
 * @author coolw
 * @date 2025/8/15 10:59
 */
public class PoolTest {

    public static void main(String[] args) throws Exception {
        // 构建连接池参数
        GenericObjectPoolConfig<Connection> poolConfig = buildPoolConfig();

        // 获取对象连接池工厂
        DataSourcePooledObjectFactory factory = getPooledObjectFactory();

        // 创建通用连接池对象
        GenericObjectPool<Connection> pool = new GenericObjectPool<>(factory, poolConfig);

        // 使用连接池
        usePool(pool, true);

        // 模拟等待池对象回收
        while (true) {
        }
    }

    private static GenericObjectPoolConfig<Connection> buildPoolConfig() {
        GenericObjectPoolConfig<Connection> poolConfig = new GenericObjectPoolConfig<>();
        // 连接池中最大对象总数（活跃+空闲）
        poolConfig.setMaxTotal(4);
        // 最大空闲对象数，超过的对象会被销毁
        poolConfig.setMaxIdle(2);
        // 最小空闲对象数，连接池会主动创建对象且维护此数据
        poolConfig.setMinIdle(0);
        // 获取对象的最大等待时间（毫秒），超时会抛出异常
        poolConfig.setMaxWait(Duration.ofMillis(2000L));
        // 空闲对象最小存活时间（毫秒），超时可能会被回收（测试设置10秒，---- 120000L）
        poolConfig.setMinEvictableIdleDuration(Duration.ofMillis(10000L));
        // 空闲对象回收线程运行的间隔时间（毫秒）
        poolConfig.setTimeBetweenEvictionRuns(Duration.ofMillis(60000L));
        // 借用对象时验证对象有效性，开启会降低性能（如果对并发有要求，生产不建议开启）
        poolConfig.setTestOnBorrow(false);
        // 归还对象时验证对象有效，开启会降低性能（如果对并发有要求，生产不建议开启）
        poolConfig.setTestOnReturn(true);
        // 空闲对象检测时验证对象有效性，开启会检查空闲对象是否超过minEvictableIdleTimeMillis或者无效，定期回收对象，
        poolConfig.setTestWhileIdle(true);
        // ... 其他参数
        return poolConfig;
    }

    private static DataSourcePooledObjectFactory getPooledObjectFactory() {
        // 数据库连接配置
        DataSourceConfig config = DataSourceConfig.create("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=Asia/Shanghai"
                , "root"
                , "123456");
        return new DataSourcePooledObjectFactory(config);
    }

    private static void usePool(GenericObjectPool<Connection> pool, boolean isAsync) throws Exception {
        if (isAsync) {
            asyncUsePool(pool);
        } else {
            syncUsePool(pool);
        }
    }

    /**
     * 同步使用连接池对象，由于是同步借用/归还，所以只会有1个对象创建，然后反复使用
     */
    private static void syncUsePool(GenericObjectPool<Connection> pool) throws Exception {
        Connection connection = null;
        for (int i = 1; i <= 10; i++) {
            String threadName = Thread.currentThread().getName() + "_" + i;
            try {
                // 从连接池中借用对象
                connection = pool.borrowObject();
                print(threadName, pool, connection, true);
            } finally {
                if (connection != null) {
                    // 将对象归还到连接池
                    pool.returnObject(connection);
                    print(threadName, pool, connection, false);
                }
            }
        }
    }

    private static void asyncUsePool(GenericObjectPool<Connection> pool) {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        try {
            for (int i = 1; i <= 10; i++) {
                int finalI = i;
                executorService.execute(() -> {
                    Connection connection = null;
                    String threadName = Thread.currentThread().getName() + "_" + finalI;
                    try {
                        // 从连接池中借用对象
                        connection = pool.borrowObject();
                        print(threadName, pool, connection, true);

                        // 模拟阻塞，创建新的对象
                        Thread.sleep(10000L);
                    } catch (Exception e) {
                        throw new RuntimeException("借用对象发生异常", e);
                    } finally {
                        if (connection != null) {
                            // 将对象归还到连接池
                            pool.returnObject(connection);
                            print(threadName, pool, connection, false);
                        }
                    }
                });
            }
        } finally {
            executorService.shutdown();
        }
    }

    /**
     * 打印
     *
     * @param threadName 线程名称
     * @param pool       数据库连接池
     * @param connection 数据库连接对象
     * @param isBorrow   是否借用对象，true-借用对象，false-归还对象
     */
    private static void print(String threadName, GenericObjectPool<Connection> pool, Connection connection, boolean isBorrow) {
        if (isBorrow) {
            System.out.println(getCurrentDateTime() + " [" + threadName + "] 借用对象:" + connection + ", numActive:" + pool.getNumActive() + ", numIdle:" + pool.getNumIdle());
        } else {
            System.out.println(getCurrentDateTime() + " [" + threadName + "] 归还对象:" + connection + ", numActive:" + pool.getNumActive() + ", numIdle:" + pool.getNumIdle());
        }
    }

    private static String getCurrentDateTime() {
        return DateUtil.formatLocalDateTime(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss");
    }
}
